/*
	|=====|	|	|
	|	|	|	|
	|=====|	|=====|
	|	|	|	|
	|=====|	|	|
	
	BASEHACK
	Code by "Brwarner" STEAM_0_1_12243023
	
	Initiation file for serverside of gamemode
*/

//Add clientside files so clients download correct files
AddCSLuaFile("shared.lua");
AddCSLuaFile("cl_init.lua");
AddCSLuaFile('class.lua');
AddCSLuaFile('cl_hud.lua');
AddCSLuaFile('config.lua');

//Includes
include('config.lua');
include('shared.lua');
include('phases.lua');

//Find all lua files in the vgui directory
local vgui_files = file.FindInLua("gamemode/vgui/*.lua");

//Add all vgui files to the clientside lua list
for key, val in pairs(vgui_files) do
	AddCSLuaFile("gamemode/vgui/" .. val);
end

//How many blocks each team owns (without cores/comps)
block_counts = {0, 0};

//Called to make a player move to their spawnpoint
function PlayerRespawn( ply )
	//Find a suitable spawn point
	local spawn_ent = FindPlayerSpawn( ply );
	
	//Set the players position and spawn to it
	ply:SetPos(spawn_ent:GetPos());
	ply:SetAngles(spawn_ent:GetAngles());
	
	//Call loadout for player
	gamemode.Call("PlayerLoadout", ply );
end
hook.Add("PlayerRespawn", "BHRespawn", PlayerRespawn);

//Called when a player should get their weapons
function Loadout( pl )
	//If in combat phase
	if(phaseNum == 1) then
		//Give the player weapons based on their class id
		Class:GivePlayerWeapons(pl);
	else
		//Otherwise give them just the buildgun
		pl:StripWeapons();
		pl:RemoveAllAmmo();
		pl:Give("weapon_buildgun");
		pl:Give("weapon_remover");
	end
	return true;
end
hook.Add("PlayerLoadout", "BHLoadout", Loadout);

//Called to automatically assign a player to a team based on which team has the least people
function AssignTeam( ply )
	print("Team Assigment");
	//Get the number of players on each team
	local t1 = team.NumPlayers( 1 );
	local t2 = team.NumPlayers( 2 );
	
	//If team 1 has more
	if( t1 > t2 ) then
		//Assign to their team
		ply:SetTeam( 2 );
	else
		//Otherwise set to team 2
		ply:SetTeam( 1 );
	end
end

//Called when a player joins the server
function PlayerJoin( ply )
	//Give them auto-team assigment
	AssignTeam( ply );
	
	//Set their class to default "Builder"
	Class:SetPlayerClass(ply, "Constructor");
	
	//Send them block count data
	umsg.Start("UpdateLimit", ply);
		umsg.Long(block_counts[1]);
		umsg.Long(block_counts[2]);
	umsg.End();
	return true;
end
hook.Add("PlayerInitialSpawn", "BHInitSpawn", PlayerJoin);

//Console command to change a player's team
function ChangeTeam(ply, cmd, args)
	local t = tonumber(args[1]);
	if(t > 0 && t < 3) then
		if(t == ply:Team()) then return; end
		//Set their team to given one
		ply:SetTeam(t);
		
		//If they are alive
		if(ply:Alive()) then
			//Sucide them to effect team change
			ply:Kill();
		end
	end
end
concommand.Add("changeTeam", ChangeTeam);

//Console command to change a player's class
function ChangeClass(ply, cmd, args)
	//Set their class
	Class:SetPlayerClass(ply, args[1]);
	
	//Inform player of class change
	ply:PrintMessage(HUD_PRINTCENTER, "Next time you respawn you will be a(n) " .. args[1]);
end
concommand.Add("changeClass", ChangeClass);

//Called whena  player presses the team menu button (F2)
function ShowTeamMenu( ply )
	//Run console command to show menu
	ply:ConCommand("showteam");
	return true;
end
hook.Add("ShowTeam", "BHShowTeamMenu", ShowTeamMenu);

//Called when the client requests a spawn point
function FindPlayerSpawn( ply )
	//Find all spawn points matching their team
	local spawns = {};
	if(ply:Team() == 1) then
		spawns = ents.FindByClass("info_player_rebel");
		table.Add(spawns, ents.FindByClass("info_player_terrorist"));
	elseif(ply:Team() == 2) then
		spawns = ents.FindByClass("info_player_combine");
		table.Add(spawns, ents.FindByClass("info_player_counterterrorist"));
	end
	
	//Get a random number and return the spawn point
	local num = math.random(#spawns);
	return spawns[num];
end
hook.Add("PlayerSelectSpawn", "BHFindSpawn", FindPlayerSpawn);

core_rebels = 0;
core_combine = 0;
//Adds each team's cores
function SetupCores()
	//Add all rebel cores
	local rebelCores = ents.FindByClass("info_core_rebel");
	for key, val in pairs(rebelCores) do
	local ent = ents.Create("bh_block");
		ent:Spawn();
		ent:SetPos(val:GetPos());
		ent:SetAngles(val:GetAngles());
		ent:SetBlock("core", 1);
		ent:SetMoveType(MOVETYPE_NONE);
		core_rebels = core_rebels + 1;
	end
	
	//Add combine cores
	local combineCores = ents.FindByClass("info_core_combine");
	for key, val in pairs(combineCores) do
	local ent = ents.Create("bh_block");
		ent:Spawn();
		ent:SetPos(val:GetPos());
		ent:SetAngles(val:GetAngles());
		ent:SetBlock("core", 2);
		ent:SetMoveType(MOVETYPE_NONE);
		core_combine = core_combine + 1;
	end
	
	//Setup computers
	
	//Rebel computers
	local rebelComps = ents.FindByClass("info_comp_rebel");
	for key, val in pairs(rebelComps) do
	local ent = ents.Create("bh_block");
		ent:Spawn();
		ent:SetPos(val:GetPos());
		ent:SetAngles(val:GetAngles());
		ent:SetBlock("comp", 1);
		ent:SetMoveType(MOVETYPE_NONE);
	end
	
	//Combine computers
	local combineComps = ents.FindByClass("info_comp_combine");
	for key, val in pairs(combineComps) do
	local ent = ents.Create("bh_block");
		ent:Spawn();
		ent:SetPos(val:GetPos());
		ent:SetAngles(val:GetAngles());
		ent:SetBlock("comp", 2);
		ent:SetMoveType(MOVETYPE_NONE);
	end
end
hook.Add("InitPostEntity", "BHCores", SetupCores);

//Called to make a team victorious
function Victory(t)
	//For each player
	for key, val in pairs(player.GetAll()) do
		//Lock in place and show the scorebaord
		val:Lock();
		val:SendLua("GAMEMODE.ShowScoreboard = true");
		
		//Inform players of winner
		local msg = "";
		if(t == 1) then msg = "Rebels WIN!"; end
		if(t == 2) then msg = "Combine WIN!"; end
		val:PrintMessage(HUD_PRINTCENTER, msg);
	end
	
	//In five seconds, switch maps
	timer.Create("bh_nextmap", 5, 1, function() hook.Call("PlayNextMap") end);
end

//Called to start next map in maplist
function NextMap()
	local next_map = nil;
	
	//Look for next map in cycle
	for key, map in pairs(mapcycle) do
		if(map == game.GetMap()) then
			next_map = mapcycle[key+1];
		end
	end
	if(next_map == nil) then next_map = mapcycle[1]; end
	
	//Change map
	game.ConsoleCommand("changegamemode " .. next_map .. " BH\n");
end
hook.Add("PlayNextMap", "BHLoadNewMap", NextMap);

//Called when a core is destroyed
function CoreDestroyed(ent)
	//Find out which team, update counters and team scores
	if(ent:GetTeam() == 1) then core_rebels = core_rebels - 1; team.AddScore(2,500); end
	if(ent:GetTeam() == 2) then core_combine = core_combine - 1; team.AddScore(1,500); end
	
	//If a team has no more cores, trigger victory
	if(core_rebels <= 0) then Victory(2); return; end
	if(core_combine <= 0) then Victory(1); return; end
end
hook.Add("CoreDestroyed", "BHCoreDestroyed", CoreDestroyed);

//Called to refresh player health/ammo based on class
function RefreshPlayer(ply)
	if(phaseNum != 0) then
		Class:GivePlayerAmmo(ply);
	end
end
hook.Add("HandleRefresh", "BHPlayerRefresh", RefreshPlayer);

//Shows a simple notice on the screen
function ShowNotice(text, ply)
	umsg.Start("ShowNotice", ply)
		umsg.String(text);
	umsg.End();
end
hook.Add("ShowNotice", "BHShowNotice", ShowNotice);

//Gets all blocks from one team, table in form of entity indicies
function GetTeamBlockList(t)
	local blocks = ents.FindByClass("bh_block");
	local team_blocks = {};
	for key, val in pairs(blocks) do
		if(val.block.Team == t) then table.insert(team_blocks, val:EntIndex()); end
	end
	
	return team_blocks;
end
hook.Add("GetTeamBlocks", "BHGetTeamBlocks", GetTeamBlockList);

//Returns if we have reached the block limit
function CheckLimit( t )
	local blocks = block_counts[t];
	if(blocks >= maxBlocks) then return false;
	else return true;
	end
end
hook.Add("CheckLimit", "BHCheckBlockLimit", CheckLimit);

//Changes the limit counter by a for team t
function IncrementCount( t, a )
	block_counts[t] = block_counts[t] + a;
	local rp = RecipientFilter();
	rp:AddAllPlayers();
	umsg.Start("UpdateLimit", rp);
		umsg.Long(block_counts[1]);
		umsg.Long(block_counts[2]);
	umsg.End();
end
hook.Add("IncrementBlocks", "BHIncrementCount", IncrementCount);

//Makes sure a player has the correct model for their team
function SetupModel( ply )
	local mdl = ply:GetModel();
	local models = {};
	if(ply:Team() == 1) then models = rebel_models; else
		models = combine_models; end
	
	for key, val in pairs(models) do
		if( mdl == val) then return; end
	end
	
	local amount_models = #models;
	local m = math.random(amount_models);
	print(models[m]);
	ply:SetModel( models[m] );
end
hook.Add("PlayerSetModel", "BHCheckModel", SetupModel);